﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Xml;

namespace Universe.WCF.Behaviors
{
    public class NetDataContractFormat : Attribute, IOperationBehavior
    {

        public static void ApplyTo(ContractDescription contractDescription)
        {
            foreach (OperationDescription operation in contractDescription.Operations)
            {
                ReplaceDataContractSerializerOperationBehavior(operation);
            }
        }

        public static void ApplyTo(ServiceHost serviceHost)
        {
            foreach (ServiceEndpoint endpoint in serviceHost.Description.Endpoints)
                ApplyTo(endpoint.Contract);
        }

        public static void ApplyTo<T>(ChannelFactory<T> factory)
        {
            CommunicationState state = factory.State;
            if (state != CommunicationState.Created)
                throw new InvalidOperationException("Channel factory Behavior could not be changed, due to channel state is " + state + ".");

            ApplyTo(factory.Endpoint.Contract);
        }


        void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
        {
        }

        void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
        {
            ReplaceDataContractSerializerOperationBehavior(description);
        }

        void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
        {
            ReplaceDataContractSerializerOperationBehavior(description);
        }

        void IOperationBehavior.Validate(OperationDescription description)
        {
        }


        private static void ReplaceDataContractSerializerOperationBehavior
            (
            OperationDescription description
            )
        {
            DataContractSerializerOperationBehavior dcsOperationBehavior =
                description.Behaviors.Find<DataContractSerializerOperationBehavior>();

            NetDataContractSerializerOperationBehavior ndcsOperationBehavior =
                description.Behaviors.Find<NetDataContractSerializerOperationBehavior>();

            if (dcsOperationBehavior != null && ndcsOperationBehavior == null)
            {
                description.Behaviors.Remove(dcsOperationBehavior);
                description.Behaviors.Add(new NetDataContractSerializerOperationBehavior(description));
            }
        }

    }

    public class NetDataContractSerializerOperationBehavior :
        DataContractSerializerOperationBehavior
    {
        public NetDataContractSerializerOperationBehavior(
            OperationDescription operationDescription) :
                base(operationDescription)
        {
        }

        public override XmlObjectSerializer CreateSerializer(
            Type type, 
            string name, 
            string ns, 
            IList<Type> knownTypes)
        {
            return new NetDataContractSerializer();
        }

        public override XmlObjectSerializer CreateSerializer
            (
            Type type,
            XmlDictionaryString name, 
            XmlDictionaryString ns,
            IList<Type> knownTypes
            )
        {
            return new NetDataContractSerializer();
        }
    }
}